home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue25 / survive / MYDS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-07-21  |  8.5 KB  |  290 lines

  1. unit MyDS;
  2.  
  3. interface
  4.  
  5. uses
  6.   Classes, DB, SysUtils;
  7.  
  8. type
  9.   PExtraRecInfo = ^TExtraRecInfo;
  10.   TExtraRecInfo = record
  11.       RecordNumber: LongInt;
  12.       BookmarkFlag: TBookmarkFlag;
  13.     end;
  14.  
  15.   TBookmarkInfo = LongInt;
  16.  
  17.   TMyDataSet = class(TDataSet)
  18.     private
  19.       FBookmarkOffset: LongInt;  { Offset to bookmark data in recbuf }
  20.       FCursorOpen: Boolean;      { True if cursor is open }
  21.       FInternalFile: file;       { File variable }
  22.       FRecSize: Word;            { Physical size of record }
  23.       FRecBufSize: Word;         { Total size of recbuf }
  24.       FExtraRecInfoOffset: Word; { Offset to extra rec info in recbuf }
  25.       FTableName: TFileName;     { External filename to open }
  26.     protected
  27.  
  28.       { basic file reading and navigation }
  29.       function AllocRecordBuffer: PChar; override;
  30.       procedure FreeRecordBuffer(var Buffer: PChar); override;
  31.       function GetCurrentRecord(Buffer: PChar): Boolean; override;
  32.       function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  33.       function GetRecordCount: Integer; override;
  34.       function GetRecordSize: Word; override;
  35.       function GetRecNo: Integer; override;
  36.       procedure InternalClose; override;
  37.       procedure InternalFirst; override;
  38.       procedure InternalLast; override;
  39.       procedure InternalOpen; override;
  40.       function IsCursorOpen: Boolean; override;
  41.  
  42.       { bookmarks }
  43.       function BookmarkValid(Bookmark: TBookmark): Boolean; override;
  44.       function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
  45.       procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  46.       function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  47.       procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  48.       procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  49.       procedure InternalGotoBookmark(Bookmark: Pointer); override;
  50.       procedure InternalSetToRecord(Buffer: PChar); override;
  51.  
  52.       { basic file modification }
  53.       procedure InternalInitRecord(Buffer: PChar); override;
  54.       procedure InternalEdit; override;
  55.       procedure InternalDelete; override;
  56.       procedure InternalPost; override;
  57.     public
  58.       {TDataSet properties}
  59.       property RecordSize: Word read GetRecordSize write FRecSize;
  60.       {descendant properties}
  61.       property TableName: TFileName read FTableName write FTableName;
  62.     end;
  63.  
  64. implementation
  65.  
  66. function TMyDataSet.AllocRecordBuffer: PChar;
  67. begin
  68.   Result := StrAlloc(FRecBufSize);
  69.   FillChar(Result^, FRecBufSize, #0);
  70. end;
  71.  
  72. function TMyDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
  73. var
  74.   DelFlag: Byte;
  75. begin
  76.   Result := Assigned(Bookmark) and
  77.             (TBookmarkInfo(Bookmark^) > 0) and
  78.             (TBookmarkInfo(Bookmark^) <= RecordCount);
  79.   if Result then begin
  80.     CursorPosChanged;  { physical position no longer matches logical position }
  81.     try
  82.       Seek(FInternalFile, TBookmarkInfo(Bookmark^) * FRecSize);
  83.       BlockRead(FInternalFile, DelFlag, 1);
  84.       Result := DelFlag = 0;  { check for a deleted record }
  85.     except
  86.       Result := False;
  87.     end;
  88.   end;
  89. end;
  90.  
  91. function TMyDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
  92. begin
  93.   { bookmarks are equal if they are both nil or they both have the same value }
  94.   if Bookmark1 = Bookmark2 then
  95.     Result := 0
  96.   else begin
  97.     Result := 1;
  98.     if Assigned(Bookmark1) and Assigned(Bookmark2) then
  99.       if TBookmarkInfo(Bookmark1^) = TBookmarkInfo(Bookmark2^) then
  100.         Result := 0;
  101.   end;
  102. end;
  103.  
  104. procedure TMyDataSet.FreeRecordBuffer(var Buffer: PChar);
  105. begin
  106.   StrDispose(Buffer);
  107. end;
  108.  
  109. procedure TMyDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
  110. begin
  111.   Move(Buffer[FBookmarkOffset], Data^, BookmarkSize);
  112. end;
  113.  
  114. function TMyDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  115. begin
  116.   Result := PExtraRecInfo(Buffer + FExtraRecInfoOffset).BookmarkFlag;
  117. end;
  118.  
  119. function TMyDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
  120. begin
  121.   Result := False;
  122.   if not IsEmpty then begin
  123.     Result := True;
  124.     Move(ActiveBuffer^, Buffer^, FRecSize);  {not sure here, buffer may contain internal data}
  125.   end;
  126. end;
  127.  
  128. function TMyDataSet.GetRecNo: Integer;
  129. begin
  130.   { Because of Delphi's internal record buffering, we must read the stored
  131.     record number, not the current physical file position }
  132.   Result := PExtraRecInfo(ActiveBuffer + FExtraRecInfoOffset)^.RecordNumber;
  133. end;
  134.  
  135. function TMyDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  136. var
  137.   FilePosition: LongInt;
  138. begin
  139.   Result := grOk;
  140.   case GetMode of
  141.     gmCurrent:
  142.       begin
  143.         Seek(FInternalFile, FilePos(FInternalFile) - FRecSize);
  144.         BlockRead(FInternalFile, Buffer^, FRecSize);
  145.         if Byte(Buffer^) <> 0 then  { deleted rec? }
  146.           Result := grError;
  147.       end;
  148.     gmNext:
  149.       { read next record, skipping deleted records }
  150.       repeat
  151.         if System.Eof(FInternalFile) then
  152.           Result := grEOF
  153.         else
  154.           BlockRead(FInternalFile, Buffer^, FRecSize);
  155.       until (Result <> grOk) or (Byte(Buffer^) = 0);
  156.     gmPrior:
  157.       repeat
  158.         FilePosition := FilePos(FInternalFile);
  159.         if FilePosition < (2 * FRecSize) then
  160.           Result := grBOF
  161.         else begin
  162.           if Eof then
  163.             Seek(FInternalFile, FileSize(FInternalFile) - FRecSize)
  164.           else
  165.             Seek(FInternalFile, FilePosition - (2 * FRecSize));
  166.           BlockRead(FInternalFile, Buffer^, FRecSize);
  167.         end;
  168.       until (Result <> grOk) or (Byte(Buffer^) = 0);
  169.     else
  170.       Result := grError;
  171.   end;
  172.  
  173.   if Result = grOk then begin
  174.     with PExtraRecInfo(Buffer + FExtraRecInfoOffset)^ do begin
  175.       RecordNumber := (FilePos(FInternalFile) div FRecSize) - 1;
  176.       BookmarkFlag := bfCurrent;
  177.       SetBookmarkData(Buffer, @RecordNumber);
  178.     end;
  179.   end;
  180. end;
  181.  
  182. function TMyDataSet.GetRecordCount: Integer;
  183. begin
  184.   Result := FileSize(FInternalFile) div FRecSize;
  185. end;
  186.  
  187. function TMyDataSet.GetRecordSize: Word;
  188. begin
  189.   Result := FRecSize;
  190. end;
  191.  
  192. procedure TMyDataSet.InternalClose;
  193. begin
  194.   CloseFile(FInternalFile);
  195.   FCursorOpen := False;
  196. end;
  197.  
  198. procedure TMyDataSet.InternalDelete;
  199. var
  200.   DelFlag: Byte;
  201.   FilePosition: LongInt;
  202. begin
  203.   FilePosition := FilePos(FInternalFile) - FRecSize;
  204.   Seek(FInternalFile, FilePosition);
  205.   DelFlag := 255;
  206.   BlockWrite(FInternalFile, DelFlag, 1);
  207.   Seek(FInternalFile, FilePosition + FRecSize);
  208. end;
  209.  
  210. procedure TMyDataSet.InternalEdit;
  211. begin
  212.   { Refresh the current record }
  213.   Seek(FInternalFile, FilePos(FInternalFile) - FRecSize);
  214.   BlockRead(FInternalFile, ActiveBuffer^, FRecSize);
  215. end;
  216.  
  217. procedure TMyDataSet.InternalFirst;
  218. begin
  219.   Seek(FInternalFile, 0);
  220. end;
  221.  
  222. procedure TMyDataSet.InternalInitRecord(Buffer: PChar);
  223. begin
  224.   FillChar(Buffer^, FRecBufSize, #0);
  225. end;
  226.  
  227. procedure TMyDataSet.InternalGotoBookmark(Bookmark: Pointer);
  228. { position physical file to bookmarked record }
  229. begin
  230.   { Position AFTER the record, as though we just read it }
  231.   Seek(FInternalFile, (TBookmarkInfo(Bookmark^) + 1) * FRecSize);
  232. end;
  233.  
  234. procedure TMyDataSet.InternalLast;
  235. begin
  236.   Seek(FInternalFile, FileSize(FInternalFile));  { force eof condition }
  237. end;
  238.  
  239. procedure TMyDataSet.InternalOpen;
  240. begin
  241.   BookmarkSize := SizeOf(TBookmarkInfo);
  242.   FRecBufSize := FRecSize + SizeOf(TExtraRecInfo) + BookmarkSize;
  243.  
  244.   FExtraRecInfoOffset := FRecSize;
  245.   FBookmarkOffset := FExtraRecInfoOffset + SizeOf(TExtraRecInfo);
  246.  
  247.   AssignFile(FInternalFile, FTableName);
  248.   Reset(FInternalFile, 1);   { Open a file of bytes }
  249.   FCursorOpen := True;
  250. end;
  251.  
  252. procedure TMyDataSet.InternalPost;
  253. begin
  254.   case State of
  255.     dsEdit:
  256.       begin
  257.         Seek(FInternalFile, FilePos(FInternalFile) - FRecSize);
  258.         BlockWrite(FInternalFile, ActiveBuffer^, FRecSize);
  259.       end;
  260.     dsInsert:
  261.       begin
  262.         Byte(ActiveBuffer^) := 0;  { reset deleted flag }
  263.         Seek(FInternalFile, FileSize(FInternalFile));
  264.         BlockWrite(FInternalFile, ActiveBuffer^, FRecSize);
  265.       end;
  266.   end;
  267. end;
  268.  
  269. procedure TMyDataSet.InternalSetToRecord(Buffer: PChar);
  270. begin
  271.   InternalGotoBookmark(Buffer + FBookmarkOffset);
  272. end;
  273.  
  274. function TMyDataSet.IsCursorOpen: Boolean;
  275. begin
  276.   Result := FCursorOpen;
  277. end;
  278.  
  279. procedure TMyDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
  280. begin
  281.   Move(Data^, Buffer[FBookmarkOffset], BookmarkSize);
  282. end;
  283.  
  284. procedure TMyDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  285. begin
  286.   PExtraRecInfo(Buffer + FExtraRecInfoOffset).BookmarkFlag := Value;
  287. end;
  288.  
  289. end.
  290.